package io.xxx.cyb.sync.impl

import com.alibaba.fastjson.JSONObject
import io.xxx.cyb.model.Document
import io.xxx.cyb.model.Store
import io.xxx.cyb.model.StoreJob
import io.xxx.cyb.sync.Porter
import io.xxx.cyb.util.dateTimeFormatter
import io.xxx.cyb.util.toLocalDateTime
import io.xxx.cyb.util.zoneOffset
import org.apache.commons.codec.digest.DigestUtils
import java.time.Duration
import java.time.LocalDateTime
import java.util.*

/**
 * 贝贝
 */
abstract class BbPorter(sid: String) : Porter(sid) {

    private val httpUrl = "http://api.open.beibei.com/outer_api/out_gateway/route.html"

    protected abstract val method: String

    override val pageSize = 300

    protected fun getResponse(
        store: Store,
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        timeRange: Any?,
        pageNo: Long = 1,
    ): JSONObject {
        val request = TreeMap(buildRequest(startTime, endTime, timeRange as String, pageNo))
        request["method"] = method
        request["timestamp"] = LocalDateTime.now().toEpochSecond(zoneOffset).toString()
        request["app_id"] = store.appKey!!
        request["session"] = store.accessToken!!
        val builder = StringBuilder()
            .append(store.appSecret)
            .append(request.entries.joinToString("") { it.key + it.value })
            .append(store.appSecret)
        val sign = DigestUtils.md5Hex(builder.toString()).uppercase()
        request["sign"] = sign
        val response = restTemplate.postForEntity(httpUrl, request, JSONObject::class.java).body!!
        val errCode = response.getString("err_code")
        if (errCode != null) {
            throwException(javaClass.simpleName, method, errCode, response.getString("err_msg"))
        }
        return response
    }

    private fun buildRequest(
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        timeRange: String,
        pageNo: Long = 1
    ): Map<String, String> {
        return mapOf(
            "time_range" to timeRange,
            "start_time" to startTime.format(dateTimeFormatter),
            "end_time" to endTime.format(dateTimeFormatter),
            "page_no" to pageNo.toString(),
            "page_size" to pageSize.toString(),
        )
    }
}

// 订单
@Suppress("unused")
open class BbTradePorter(sid: String) : BbPorter(sid) {

    override val method = "beibei.outer.trade.order.get"

    override val delay: Duration = Duration.ofMinutes(30)

    override fun getParameters(store: Store): Pair<List<Any>, Boolean> {
        return listOf("modified_time") to false
    }

    override fun getStartTime(storeJob: StoreJob): LocalDateTime {
        val minStartTime = LocalDateTime.now().minusDays(30).plusMinutes(1)
        return if (storeJob.endTime!! < minStartTime) minStartTime else storeJob.endTime!!
    }

    override fun getCount(store: Store, startTime: LocalDateTime, endTime: LocalDateTime, parameter: Any?): Long? {
        val response = getResponse(store, startTime, endTime, parameter)
        return response.getLong("count")
    }

    override fun getData(
        store: Store,
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        parameter: Any?,
        pageNo: Long,
    ): List<Document>? {
        val response = getResponse(store, startTime, endTime, parameter)
        return response.getJSONArray("data")?.map {
            @Suppress("unchecked_cast")
            val value = JSONObject(it as Map<String, Any>)
            Document(
                value.getString("oid"),
                value,
                value.getString("modified_time").toLocalDateTime()
            )
        }
    }
}

@Suppress("unused")
class BbTradeProcurator(sid: String) : BbTradePorter(sid) {

    override val cron = "0/10 * * * * ?"

    override fun getParameters(store: Store): Pair<List<Any>, Boolean> {
        return listOf("create_time") to false
    }
}

// 退单
@Suppress("unused")
class BbRefundPorter(sid: String) : BbPorter(sid) {

    override val method = "beibei.outer.refunds.get"

    override fun getParameters(store: Store): Pair<List<Any>, Boolean> {
        return listOf("modified_time") to false
    }

    override fun getCount(store: Store, startTime: LocalDateTime, endTime: LocalDateTime, parameter: Any?): Long? {
        val response = getResponse(store, startTime, endTime, parameter)
        return response.getLong("count")
    }

    override fun getData(
        store: Store,
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        parameter: Any?,
        pageNo: Long,
    ): List<Document>? {
        val response = getResponse(store, startTime, endTime, parameter, pageNo)
        return response.getJSONArray("data")?.map {
            @Suppress("unchecked_cast")
            val value = JSONObject(it as Map<String, Any>)
            Document(
                value.getString("id"),
                value.getString("oid"),
                value,
                value.getString("modified_time").toLocalDateTime()
            )
        }
    }
}

@Suppress("unused")
class BbRefundProcurator(sid: String) : BbTradePorter(sid) {

    override val cron = "0/10 * * * * ?"

    override fun getParameters(store: Store): Pair<List<Any>, Boolean> {
        return listOf("create_time") to false
    }
}